import Foundation
import GomokuCommon

// 服务器
class Server {
    private var mOnlineUsers: Dictionary<String, Client>    // 在线用户列表
    private var mSocket: Int32                              // 套接字
    private var mThread: Thread?                            // 线程

    /**
     * 构造方法。
     */
    public init() {
        mOnlineUsers = Dictionary<String, Client>()
        mSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
    }

    /**
     * 析构方法。
     */
    deinit {
        // 关闭所有在线用户
        for onlineUser in mOnlineUsers {
            onlineUser.value.close()
        }
        
        // 清空在线用户列表
        mOnlineUsers.removeAll()
        
        // 停止线程
        mThread?.cancel()
        
        // 关闭套接字
        Darwin.close(mSocket)
    }
    
    /**
     * 处理寻找其它用户请求。
     * @param request   请求
     * @param client    客户端
     */
    private func handleFindUsers(_ request: FindUsersRequest, client: Client) {
        // 创建结果
        let rivalList = NSMutableArray()
        for item in mOnlineUsers {
            // 跳过自己的名字
            if request.mUsername == item.key {
                continue
            }
            rivalList.add(item.key)
        }
        let result = FindUsersResult(rivalList)

        // 发送结果
        client.sendData(result)
    }
    
    /**
     * 处理登录请求。
     * @param request   请求
     * @param client    客户端
     */
    private func handleLogon(_ request: LogonRequest, client: Client) {
        var result: LogonResult

        // 如果已登录，则直接返回
        let onlineUser = mOnlineUsers[request.mUsername!]
        if onlineUser != nil {
            result = LogonResult("")
            client.sendData(result)
            return
        }
        
        // 保存用户
        mOnlineUsers[request.mUsername!] = client

        // 创建结果
        result = LogonResult(request.mUsername!)

        // 发送结果
        client.sendData(result)
    }

    /**
     * 处理协议。
     * @param pro   协议
     */
    public func handleProtocol(_ pro: GomokuProtocol) {
        // 如果目标用户不在线则返回
        let rivalUser = mOnlineUsers[pro.mUsername!]
        if rivalUser == nil {
            return
        }

        // 发送给目标用户
        rivalUser!.sendData(pro)
    }
    
    /**
     * 处理退出游戏请求。
     * @param request   请求
     * @param client    客户端
     */
    private func handleQuitGame(_ request: QuitGame, client: Client) {
        // 广播该用户的请求
        for rivalUser in mOnlineUsers.values {
            rivalUser.sendData(request)
        }

        // 将该用户从在线列表移除
        mOnlineUsers.removeValue(forKey: request.mUsername!)

        // 关闭客户端
        client.close()
    }

    /**
     * 处理客户端发送来的数据。
     * @param pro       协议
     * @param client    客户端
     */
    public func onGetMessageFromClient(_ client: Client, pro: GomokuProtocol) {
        switch pro.mType {
            case .FIND_USERS:
                self.handleFindUsers(pro as! FindUsersRequest, client: client)
            case .LOGON:
                self.handleLogon(pro as! LogonRequest, client: client)
            case .QUIT_GAME:
                self.handleQuitGame(pro as! QuitGame, client: client)
            default:
                self.handleProtocol(pro)
        }
    }

    /**
     * 服务器线程。
     */
    @objc private func server_thread() {
        var addr = sockaddr()
        var len = socklen_t(MemoryLayout<sockaddr>.size)
        var clientSocket: Int32
        
        while true {
            clientSocket = accept(mSocket, &addr, &len)
            _ = Client(self, sock: clientSocket)
        }
    }
    
    /**
     * 开启服务。
     */
    public func start() -> Bool {
        // 绑定 IP 地址和端口号
        var addr = sockaddr_in()
        memset(&addr, 0, MemoryLayout<sockaddr_in>.size)
        addr.sin_len = __uint8_t(MemoryLayout<sockaddr_in>.size)
        addr.sin_family = sa_family_t(AF_INET)
        addr.sin_port = _OSSwapInt16(GomokuProtocol.SERVER_PORT)
        addr.sin_addr.s_addr = inet_addr(GomokuProtocol.SERVER_IP)
        var serverAddr = sockaddr()
        memcpy(&serverAddr, &addr, MemoryLayout<sockaddr_in>.size)
        var result = bind(mSocket, &serverAddr, socklen_t(MemoryLayout<sockaddr>.size))
        if result != 0 {
            return false
        }
        
        // 侦听
        result = listen(mSocket, SOMAXCONN)
        if result != 0 {
            return false
        }
        
        // 开启接收数据的线程
        mThread = Thread(target: self, selector: #selector(server_thread), object: nil)
        mThread?.start()
        
        return true
    }
}
